-- 2022-8-18

---[[ 20.3 库定义相关的元方法

    -- 到目前的为止 我们见过的所有元方法针对的都是核心Lua语言
    -- Lua语言虚拟机(virtual machine)会检测一个操作中涉及的值是否有存在对应元方法的元表 不过 由于元表是一个普通的表 所以任何人都可以使用它们
    -- 因此 程序库在元表中定义和使用它们自己的字段也是一种常见的实践

    -- 函数tostring就是一个典型的例子 正如我们此前所看到的 函数tostring能将表示为一种简单的文本格式
    print({}) --> table:0x8062ac0

    -- 函数print总是调用tostring来进行格式化输出 不过 当对值进行格式化时 函数tostring会首先检查值是否有一个元方法 __tostring
    -- 如果有 函数tostring就调用这个元方法来完成工作 将对象作为参数传给该函数 然后把元方法的返回值作为函数tostring的返回值

    -- 在之前集合的示例中 我们已经定义了一个将集合表示为字符串的函数 因此 只需要在元表中设置__tostring字段:
    mt.__tostring = Set.tostring

    -- 之后 当以集合作为参数调用函数print时 print就会调用函数tostring tostring又会调用Set.tostring:
    s1 = Set.new{10,4,5}
    print(s1) --> {4,5,10}

    -- 函数setmetatable和getmetatable也用到了元方法 用于保护元表
    -- 假设想要保护我们的集合 就要使用户既不能看到也不能修改集合的元表 如果在元表中设置 __metatable字段 那么getmetatable会返回这个字段的值 而setmetatable则会引发一个错误
    mt.__metatable = "not your business"
    s1 = Set.new{}
    print(getmetatable(s1)) --> not your business
    setmetatable(s1,{})
    --  stdin:1: cannot change protected metatable

    -- 从Lua5.2开始 函数pairs也有了对应的元方法 因此我们可以修改表被遍历的方式和为非表的对象增加遍历行为
    -- 当一个对象拥有__pairs元方法时 pairs会调用这个元方法来遍历
--]]